WordPress Menu Tricks

In this post I’m going to show you how to take WordPress Menu Editing to the next level. You’re going to learn how to use Primary and Secondary menus in your WordPress theme; Add descriptive sub-title links to your menu items like some popular WordPress themes and sites; Filter the menu of a WordPress theme; Add special CSS classes to wp_page_menu; and finally, how to hand-code your own WordPress menu for the ultimate in control.

Here’s how to make your WordPress menu jump through hoops.

Primary and Secondary Menus

This one’s really easy with wp_page_menu. Instead of adding it once to our theme, we’ll add it twice!—but including only select pages each time.

<?php wp_page_menu('exclude=24,27,28&amp;menu_class=menu menu-primary'); ?>
<?php wp_page_menu('include=24,27,28&amp;menu_class=menu menu-secondary'); ?>

Because wp_page_menu generates all the code we’ll need for a menu we simply have to exclude pages from the first menu and include some in the other. With new menu classes of menu-primary and menu-secondary we’re all set.

Here’s how to do it with a Thematic Child Theme—or any in any Child Theme where the parent theme is using wp_page_menu. Also easy, but with wp_list_pages so we can avoid any CSS styling problems with multiple drop down menus. Just use this code snippet in your Child Theme functions file.

// Remove the default Thematic Access
function remove_thematic_actions() {
    remove_action('thematic_header','thematic_access',9);
}
add_action('init','remove_thematic_actions');

// Recreate the Thematic Access with menu-primary and menu-secondary
function childtheme_page_menu() { ?>
	<div id="access">
		<div class="skip-link"><a href="#content" title="<?php _e('Skip navigation to the content', 'thematic'); ?>"><?php _e('Skip to content', 'thematic'); ?></a></div>        
        <?php wp_page_menu('exclude=24,27,28&amp;menu_class=menu menu-primary'); ?>
        <div class="menu menu-secondary">
            <ul>
                <?php wp_list_pages('title_li=&amp;include=24,27,28'); ?>
            </ul>
        </div>
    </div><!-- #access -->    
<?php }
add_action('thematic_header','childtheme_page_menu','9');

Adding Sub-Titles To Menu Links

Sometimes you’re going to want to add descriptive text underneath your WordPress menu links like in the Grid Focus theme,

grid-focus

or like you’ve seen around the web on static, and non-WordPress sites.

creative-display

Usually this is done by hand editing the menu but it can be accomplished using WordPress custom fields. Simply add a custom field to the pages in question named “subtitle” with a value of “whatever you want your descriptive subtitle text to be”. Then make sure you have the following code snippet (adapted from the clearskys.net blog ) in the functions file of your WordPress Child Theme—as long as the parent theme is using wp_page_menu. If you’re simply editing a WordPress theme directly, cut the code from the childtheme_page_menu function and use it to replace your existing menu in header.php.

// Adds descriptive text to link titles
// With help from http://blog.clearskys.net/2008/12/17/how-to-adding-menu-sub-titles-to-a-theme/
function sub_page_list() {
global $wpdb;
$sql = "SELECT p.ID, p.post_title, p.guid, pm.meta_value FROM " . $wpdb->posts . " AS p LEFT JOIN ";
$sql .= "(SELECT post_id, meta_value FROM " . $wpdb->postmeta . " AS ipm WHERE meta_key = ‘subtitle’) ";
$sql .= "AS pm ON p.ID = pm.post_id ";
$sql .= "WHERE p.post_type = ‘page’ AND p.post_parent = 0 AND p.post_status = ‘publish’ ";
$sql .= "ORDER BY p.menu_order ASC ";
$sql .= "LIMIT 0, 10";
$rows = $wpdb->get_results($sql,OBJECT);
if($rows) {
foreach($rows as $row) {
echo "<li>";
$link_url = get_permalink($row->ID);
echo "<a href="$link_url"" . "">$row->post_title</a>";
echo "<span style="display:block;">$row->meta_value</span>";
echo "</li>";
}
}
}

// Filter the menu to add the list
function childtheme_page_menu() { ?>
<div class="menu">
<ul>
<?php if (is_front_page()) { ?>
<li><a href="<?php bloginfo(‘home’) ?>/" title="<?php echo wp_specialchars( get_bloginfo(‘name’), 1 ) ?>" rel="home">
Home <span style="display:block;">This is the home page</span>
</a></li>
<?php } else { ?>
<li><a href="<?php bloginfo(‘home’) ?>/" title="<?php echo wp_specialchars( get_bloginfo(‘name’), 1 ) ?>" rel="home">
Home <span style="display:block;">Return to the home page</span>
</a></li>
<?php } ?>

<?php sub_page_list(); ?>

</ul>
</div>
<?php }
add_filter(‘wp_page_menu’,’childtheme_page_menu’);

Adding Class To Your Menu List

One of the problems of using wp_page_menu is that the HTML is all wrapped in the function. You can’t get at it. Which is a pain if you want to use the function but also want to add special classes for implementing javascript drop-down menus or do some trickier CSS without giving yourself a headache. Luckily, you can filter it. Here’s how to filter wp_page_menu and add a unique class to the first instance of the ul tag, using PHP’s preg_replace to find the first ul tag and adding in id and class attributes.

// Add ID and CLASS attributes to the first <ul> occurence in wp_page_menu
function add_menuclass($ulclass) {
return preg_replace('/<ul>/', '<ul id="nav" class="something-classy">', $ulclass, 1);
}
add_filter('wp_page_menu','add_menuclass');

Basically, wp_page_menu can be filtered with any content (I often use “bacon” as test content—mmm, bacon) which is handy to know if you ever want to code your own custom menu.

Hand Coding a Dynamic WordPress Menu

And sometimes you are going to want to code your own menu. I’ve done it and I’ll probably do it again. You could just create a simple list of links but it’s better to create a dynamic list that checks to see if you’re currently on a particular page and adds a class of current_page_item to the list item if you are. This’ll let you create more usable menus with a little bit of CSS.

If you’re doing this in a Child Theme and the Parent Theme is using wp_page_menu this’ll be pretty straight forward, we’re just going to filter wp_page_menu. And if you’re editing your theme directly just grab the code out of the function in this snippet and plop it into the appropriate spot of your theme.

function childtheme_menu() { ?>
<div id="menu">
<ul>
	<li class="<?php if ( is_page('about') ) { ?>current_page_item<?php } else { ?>page_item<?php } ?>"><a href="<?php echo get_option('home') ?>/about/" title="About This Blog">About</a></li>
	<li class="<?php if ( is_page('advertising') ) { ?>current_page_item<?php } else { ?>page_item<?php } ?>"><a href="<?php echo get_option('home') ?>/advertising/" title="Advertise on My Blog">Advertise</a></li>
	<li class="<?php if ( is_page('contact') ) { ?>current_page_item<?php } else { ?>page_item<?php } ?>"><a href="<?php echo get_option('home') ?>/contact/" title="Contact Me">Contact</a></li>
</ul>
</div>
 
<?php }
 
add_filter( 'wp_page_menu', 'childtheme_menu' );

The key in this example is what’s happening in the list items between the li tags. If you look at line 4 you’ll see we’re checking to see if we’re on the about page, and if we are we get a class of current_page_item, otherwise, page_item. Simple!

57 responses

  1. Probably the most interesting tip is how to add the link descriptions in the menu. That always looks cool (if done right) 🙂

  2. Thank you for the tricks. I’ll definitely use this for my projects.

  3. Something to add to make it more complete is how to filter the list and add nofollow to all, or even specific links based upon an array, which can then be done conditionally based upon page type.

  4. Great article Ian, very useful and “catchy”!

  5. Great blog! I love the formatting tweaks that really help a site to stand out while making it easier to update.
    Just a quick FYI: The email version of the blogs do not wrap the text properly. Could be the header/footer image width, or a fixed width encoded somewhere.

    Best wises!

  6. Thematic is a great theme. I’ve just posted an article on my blog about customizing the menu, starting from Justin Tadlock interesting solution (not for thematic).

    The article is in italian but I’ve tried to arrange a short translation in english.

    The code I wrote / adapted can be changed to add the sub-title as shown in your article, using link description.

  7. Daniel Young Avatar
    Daniel Young

    Thanks for posting this. Just to let you know, the code for “Adding Sub-Titles To Menu Links” produces a small bug. Line 16:


    echo "$row->post_title";

    produces double quotes after the URL (which invalidates XHTML), something like this:


    About UsCompany History

    Also, the title attribute is only preserved for the “Home” link. How would I modify the code so that it displays the title attribute for the other links as well?

    Thanks!

  8. […] 1. 40 nuevos temas gratuitos para WordPress. 2. Social Networking con BuddyPress. 3. Cómo lanzar tu propio tema de WordPress. 4. 30 temas creados en Marzo de 2009 para WordPress. 5. Trucos para menús en WordPress. […]

  9. […] idea,欢迎和大家一起分享!!! 本文由辐射鱼翻译自:WordPress Menu Tricks(有删节) 站趣译文:wp_page_menu […]

  10. Great post! I was just thinking of a way to solve sub-titles in menus for WordPress and got really happy when I found your example.

  11. […] Adding drop down’s on the menu for the children […]

  12. Getting rather attached to the Simple Sidebar Navigation plugin:
    http://www.ibsteam.net/blog/web-development/simple-sidebar-navigation-plugin-wordpress

    Has the makings of a rather flexible navigation plugin project.
    Definitely prefer it to the (now defunct) NAVT plugin.

    Do you think that this kind of plugin-based solution is tenable for robust navigation?

  13. Great tips!

    I also noticed the double quotes in Adding Sub-Titles To Menu Links that Daniel mentioned. Here’s how I fixed it, and also how I modified the element. I wanted the as part of the link element for styling purposes, and I also moved the "display:block" declaration and put it into the style rule. Here’s my tweaked version, lines 16-17:

    echo "$row->post_title";
    echo "$row->meta_value
    ";

    1. Somehow my code snipped got messed up. I’ll try again:

      echo "$row->post_title";
      echo "$row->meta_value
      ";

    2. Darn! Not working! How do we post code snippets in comments?

    3. I found that the code in Adding Sub-Titles To Menu Links seems to be incompatible with the Exclude Pages Plugin.

  14. Thanks, useful. But I think I find it easier to do menus with a custom loop and designated custom fields indicating the menu level and additional menu content, like images, for a graphic menu. The Flutter-plugin is occasionally helpful here. It’s easier to change around in page-management than in the theme. After all, you’re still excluding and including pages in the theme, not in the backend.

    1. would like to have a look at your custom loop solution if you don’t mind sharing? Any links to code?

    2. @ts would like to have a look at your custom loop solution if you don’t mind sharing? Any links to code?

  15. Oh cool, I’ve been scouring the web looking for a wordpressified hack to achieve this. I reckon it’s a plugin which would get some real attention should anyone develop something, hint hint! 🙂

    I was trying to achieve the same thing using the link_after parameter for wp_list_pages, btu it doesn’t automatically allow a custom field value in the code 😦

    Anyone know of something which could solve that?

  16. Aah shucks, seems my understanding of this code is a little too poor to mod it for a normal non-thematic non-child theme.

    I also tried the functions.php solution from clearskys btu ended up with blank page.
    I’m gonna live with writing sub title in the WordPress title field for now, works ok if u style it correctly. Just means I have to generate my head title using a custom field or something.

    I was really hoping to could somehow add a custom field value in span tags within the link_after parameter for wp_list_pages, seems it’s not possible. 😦

  17. Ben fiddling around with a few solutions like this over the last few days. Finally got the clearskys.net function working with the help of @urbanrenewal who modified the code a little.

    So no there’s the problem of the dynamic classes, no childpage drop downs & exclude page functionality not working.

    Anyone know what can be thrown in between lines 17 & 18 to echo the child page UL for any child pages?

    1. Anthony Calzadilla Avatar
      Anthony Calzadilla

      I’ve been fighting with thte sub-titled links too. I’ve been trying to figure out how to put the dynamic classes BACK into the list items but so far no luck. I posted in the forums but still no help there either.

  18. Great work man! I am implementing it for one of my blogs. Thanks a lot

  19. Any ideas how to simply INclude menu items to make a nav bar, instead of EXcluding them?

    I want to build a special menu based on certain pages only.

  20. […] the original template and wanted to integrate it into the WordPress version as well. Thanks to this article from ThemeShaper, I was able to do […]

  21. does anyone know how I can add menus at the bottome of my page? I want to add “about us” or “contact us” or “FAQ” at the bottom of the page but I cant seem to figure it out.

    appreciate any help I can get.

    Thanks.

  22. […] Making a Categories Drop-Down Menu 18. WordPress: “Magazine style” Horizontal dropdown menu 19. WordPress Menu Tricks 20. Creating Two-Tiered Conditional Navigation in […]

  23. […] post I found by Shantanu one had to hack classes.php, a WordPress core file, and the second one by Theme Shaper was lacking one very essential thing, the active […]

  24. I have created something based on the sub_page_list function. New one with active class. I’ve even added som extra spice to it, hope you’ll all enjoy: http://jonkristian.no/2009/10/a-smarter-menu-for-wordpress/

  25. […] the original template and wanted to integrate it into the WordPress version as well. Thanks to this article from ThemeShaper, I was able to do […]

  26. […] I knew there had to be a better way so I kept searching. I ran across ThemeShaper and the post WordPress Menu Tricks and it looks like John Kristian took this a step further with his post A Smarter Menu for WordPress […]

  27. Hi All

    This rewriting of the menu code causes you to loose the dynamic classes. This makes styling of different menu items difficult. I have a pretty simple plug-in that allows the use of wp_page_menu(). The plug-in is a rewrite of Page Lists Plus to strip out all of the extra code to make this plug-in as simple as possible. It is available on my site if you are interested.

    Dave
    TheTemplateBlog

  28. Great article , thanks for your help

  29. […] code used to display the image links through custom fields was heavily modified from the WordPress Menu Tricks article at ThemeShaper, take a look at the functions.php file in the theme if you’re […]

  30. Is there any way to add current_page_item to sub_page_list(); without hard coded navigation.

  31. I think this is exactly what I need! Thanks!

  32. For Primary and Secondary menus, how to designate what page goes in what menu? do I use the number generated in the class code, i.e.,128 as in “page_item page-item-128”?

    sorry if this is obvious, alas, I’m PHP illiterate. thank you!

    1. @robin
      i had this same question, which i posted to the themeshaper forum.
      https://themeshaper.com/forums/topic/a-custom-menu?replies=6

  33. […] WordPress Menu Tricks (tags: wordpress menu navigation css tips tutorials tricks) Bookmarkealo […]

  34. […] how to create the navigation in your template by excluding and including the pages by page id. Read at its source This entry was posted in General. Bookmark the permalink. Post a comment or leave a trackback: […]

  35. Excellent information and ultimate tricks to do the work….

  36. WordPresser Avatar
    WordPresser

    How to remove the title tag from the menu links? It makes no sense that when you hover the mouse cursor over a menu item to show a tooltip that just contains the same text! in the menu item being hovered over.

    example, this is always generated:
    a href=”some link” title=” menu item”

    how to just generate
    a href=”some link”

    You do have it implemented without the title tag on your site Ian, so tell us how you removed it 🙂

  37. anything that has a title with “taking it to the next level” is surely wort a read.

    But is there a way to create a custom field that on rollover shows a tool tip in a yellow box?

    i assume the css would be starting with.
    #box{
    background;yellow;
    boring:bla bla bla;
    }

    then im lost!

  38. Thanks for the tips. How about adding a .class to li menu items? Say you want to dynamically highlight your menu and you have your body ID’s from the URL – now you need to have menu items (li’s) with the same class as the body ID and with css you could do something like:
    #home .home {color: red ;} to highlight your current menu item.

    So again: how do you set a class to a li menu item? 🙂

    Thanks,
    Alex

  39. Now would the perfect time to implement WordPress 3.0 custom menus to Thematic. Here is how I did it in my childtheme for Thematic:

    <a href="#content" title="">

    'primary-menu', 'menu_class' => 'sf-menu' ) );
    ?>

    <?php
    }
    add_action('thematic_header', 'childtheme_access', 4);

    My resources:
    Goodbye, headaches. Hello, menus!
    Function Reference/wp nav menu

    There might be some faults, so use with care! Sharing is caring.

    1. Toni L Avatar

      I’m trying again since it didn’t work in the first place so here comes:

      Note! Everything comes for (functions.php)

      1)First I need the remove the default header

      function remove_thematic_menu() {
      remove_action('thematic_header','thematic_access',9);
      }
      add_action('init','remove_thematic_items');

      2)Then we make the new custom menu

      add_action( 'init', 'register_my_menu' );

      function register_my_menu() {
      register_nav_menu( 'primary-menu', __( 'Primary Menu' ) );
      }

      3) Then we construct the menu

      function childtheme_access(){ ?>

      [div id=”access”]
      [div class=”skip-link”]
      [a href=”#content” title=”
      ">
      [/a]
      [/div]

      4)Here we do the magic

      [div id=”menu”]
      'primary-menu', 'menu_class' => 'sf-menu' ) ); ?>
      [/div]
      [/div]

      Note! We are replacing the real tag signs with ‘[‘ and ‘]’

  40. How can I get a dynamic class in subtitle menu for on page state ?

  41. I am working on theming my site, which I plan to use for showcasing of my work as a web-developer.

    Editing wordpress php for themes doesn’t appear to be rocket surgery, yet I am relatively new to the platform and I don’t know all ins and outs as well as functions of the api. My question is this. I create the custom menu, through a custom menu widget, and now I want to use the same function that generates the UL for the widget in another part of the site, namely in the header menu. What would it be?

    1. @Tim: I’m not sure if understand. But I would recommend you’ll do the menu registration to your function.php file first. There you define the name for your menu and you can even add multiple menus. When you’re done there you can call the menu in your template files ( index.php, page.php etc. ).

      NOTICE: Creating what to display in menus is done in WordPress admin side.

      I won’t explain more because many other have done it allready:
      __on Codex:
      Function Reference/register nav menu
      Function Reference/register nav menus
      Function Reference/wp nav menu
      __on Tutorials:
      How to add navigation menus to existing themes and preserve backward compatibility
      Excellent article on nav menus by Justin Tadlock

  42. so. since the wp 3.0 update there are a lot of new custom menu features. one being descriptions for menu items. could we get some updated advice on the easiest way to incorporate the description as sub-titles to menu links instead of using individual pages’ custom fields? thanks Ian (or anyone else that has got this working).

    p.s. i’m definitely comin back to thematic from using hybrid…don’t know why i left. thanks for creating something awesome.